import mongoose, { Document, Schema, Model } from 'mongoose';

/**
 * 审批状态枚举
 */
export enum ApprovalStatus {
  PENDING = 'pending',
  APPROVED = 'approved',
  REJECTED = 'rejected'
}

/**
 * 用户角色关联接口定义
 * 定义用户角色关联的基本属性
 */
export interface IUserRole {
  /** 用户ID */
  userId: mongoose.Types.ObjectId;
  /** 角色ID */
  roleId: mongoose.Types.ObjectId;
  /** 系统ID（支持不同系统不同角色） */
  systemId?: mongoose.Types.ObjectId;
  /** 分配人ID */
  assignedBy: mongoose.Types.ObjectId;
  /** 分配时间 */
  assignedAt: Date;
  /** 过期时间（可选） */
  expiresAt?: Date;
  /** 审批状态：pending/approved/rejected */
  approvalStatus: ApprovalStatus;
  /** 审批人ID */
  approvedBy?: mongoose.Types.ObjectId;
  /** 审批时间 */
  approvedAt?: Date;
  /** 状态：1-正常，0-禁用 */
  status: number;
  /** 创建时间 */
  createdAt: Date;
  /** 更新时间 */
  updatedAt: Date;
}

/**
 * 用户角色关联文档接口
 * 继承IUserRole接口和mongoose的Document接口
 */
export interface IUserRoleDocument extends IUserRole, Document {
  /**
   * 转换为JSON格式，用于API响应
   * @returns 格式化的用户角色关联对象
   */
  toJSON(): any;

  /**
   * 检查角色分配是否已过期
   * @returns 是否已过期
   */
  isExpired(): boolean;

  /**
   * 检查角色分配是否需要审批
   * @returns 是否需要审批
   */
  needsApproval(): boolean;

  /**
   * 审批角色分配
   * @param approvedBy 审批人ID
   * @param approved 是否通过审批
   * @returns 更新后的文档
   */
  approve(approvedBy: mongoose.Types.ObjectId, approved: boolean): Promise<IUserRoleDocument>;

  /**
   * 检查是否可以被删除
   * @returns 是否可以删除
   */
  canBeDeleted(): boolean;
}

/**
 * 用户角色关联模型接口
 * 定义静态方法
 */
export interface IUserRoleModel extends Model<IUserRoleDocument> {
  /**
   * 根据用户ID查找角色关联
   * @param userId 用户ID
   * @param systemId 系统ID（可选）
   * @returns 用户角色关联列表
   */
  findByUserId(userId: mongoose.Types.ObjectId, systemId?: mongoose.Types.ObjectId): Promise<IUserRoleDocument[]>;

  /**
   * 根据角色ID查找用户关联
   * @param roleId 角色ID
   * @param systemId 系统ID（可选）
   * @returns 用户角色关联列表
   */
  findByRoleId(roleId: mongoose.Types.ObjectId, systemId?: mongoose.Types.ObjectId): Promise<IUserRoleDocument[]>;

  /**
   * 根据系统ID查找角色关联
   * @param systemId 系统ID
   * @returns 用户角色关联列表
   */
  findBySystemId(systemId: mongoose.Types.ObjectId): Promise<IUserRoleDocument[]>;

  /**
   * 检查用户是否拥有指定角色
   * @param userId 用户ID
   * @param roleId 角色ID
   * @param systemId 系统ID（可选）
   * @returns 是否拥有角色
   */
  hasRole(userId: mongoose.Types.ObjectId, roleId: mongoose.Types.ObjectId, systemId?: mongoose.Types.ObjectId): Promise<boolean>;

  /**
   * 批量分配角色
   * @param assignments 角色分配数组
   * @returns 创建的用户角色关联列表
   */
  batchAssign(assignments: Partial<IUserRole>[]): Promise<IUserRoleDocument[]>;

  /**
   * 批量撤销角色
   * @param userId 用户ID
   * @param roleIds 角色ID数组
   * @param systemId 系统ID（可选）
   * @returns 删除的数量
   */
  batchRevoke(userId: mongoose.Types.ObjectId, roleIds: mongoose.Types.ObjectId[], systemId?: mongoose.Types.ObjectId): Promise<number>;

  /**
   * 获取用户在指定系统的所有角色
   * @param userId 用户ID
   * @param systemId 系统ID
   * @returns 角色列表
   */
  getUserRolesInSystem(userId: mongoose.Types.ObjectId, systemId: mongoose.Types.ObjectId): Promise<IUserRoleDocument[]>;

  /**
   * 获取待审批的角色分配
   * @param systemId 系统ID（可选）
   * @returns 待审批的角色分配列表
   */
  getPendingApprovals(systemId?: mongoose.Types.ObjectId): Promise<IUserRoleDocument[]>;

  /**
   * 获取即将过期的角色分配
   * @param days 提前天数
   * @returns 即将过期的角色分配列表
   */
  getExpiringRoles(days: number): Promise<IUserRoleDocument[]>;

  /**
   * 清理过期的角色分配
   * @returns 清理的数量
   */
  cleanupExpiredRoles(): Promise<number>;
}

/**
 * 用户角色关联Schema定义
 */
const UserRoleSchema = new Schema<IUserRoleDocument>({
  // 用户ID
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: [true, '用户ID不能为空'],
    index: true
  },

  // 角色ID
  roleId: {
    type: Schema.Types.ObjectId,
    ref: 'Role',
    required: [true, '角色ID不能为空'],
    index: true
  },

  // 系统ID（可选，用于多系统权限管理）
  systemId: {
    type: Schema.Types.ObjectId,
    index: true
  },

  // 分配人ID
  assignedBy: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: [true, '分配人ID不能为空'],
    index: true
  },

  // 分配时间
  assignedAt: {
    type: Date,
    required: true,
    default: Date.now,
    index: true
  },

  // 过期时间（可选）
  expiresAt: {
    type: Date,
    index: true
  },

  // 审批状态
  approvalStatus: {
    type: String,
    required: true,
    enum: Object.values(ApprovalStatus),
    default: ApprovalStatus.APPROVED,
    index: true
  },

  // 审批人ID
  approvedBy: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    index: true
  },

  // 审批时间
  approvedAt: {
    type: Date,
    index: true
  },

  // 状态：1-正常，0-禁用
  status: {
    type: Number,
    required: true,
    enum: [0, 1],
    default: 1,
    index: true
  }
}, {
  // 自动添加创建时间和更新时间
  timestamps: true,
  // 集合名称
  collection: 'user_roles',
  // 禁用版本键
  versionKey: false
});

// 复合索引
// 用户ID + 系统ID 索引（用于查询用户在特定系统的角色）
UserRoleSchema.index({ userId: 1, systemId: 1 });

// 角色ID + 系统ID 索引（用于查询角色在特定系统的用户）
UserRoleSchema.index({ roleId: 1, systemId: 1 });

// 用户ID + 角色ID + 系统ID 唯一索引（防止重复分配）
UserRoleSchema.index({ userId: 1, roleId: 1, systemId: 1 }, { unique: true });

// 状态 + 审批状态 索引（用于查询待审批的角色分配）
UserRoleSchema.index({ status: 1, approvalStatus: 1 });

// 过期时间索引（用于查询即将过期的角色）
UserRoleSchema.index({ expiresAt: 1 }, { sparse: true });

// 分配时间索引（用于按时间排序）
UserRoleSchema.index({ assignedAt: -1 });

// 分配人索引（用于查询某人分配的角色）
UserRoleSchema.index({ assignedBy: 1, assignedAt: -1 });

/**
 * 保存前的中间件
 * 验证数据完整性和业务规则
 */
UserRoleSchema.pre('save', function(next) {
  // 如果审批状态为已审批，但没有审批人和审批时间，则自动设置
  if (this.approvalStatus === ApprovalStatus.APPROVED && !this.approvedBy) {
    this.approvedBy = this.assignedBy;
    this.approvedAt = new Date();
  }

  // 如果审批状态为待审批，清除审批人和审批时间
  if (this.approvalStatus === ApprovalStatus.PENDING) {
    this.approvedBy = undefined;
    this.approvedAt = undefined;
  }

  next();
});

/**
 * 更新前的中间件
 * 验证更新操作的合法性
 */
UserRoleSchema.pre(['updateOne', 'findOneAndUpdate'], function(next) {
  const update = this.getUpdate() as any;
  
  if (update.$set) {
    // 如果更新审批状态为已审批，自动设置审批时间
    if (update.$set.approvalStatus === ApprovalStatus.APPROVED && !update.$set.approvedAt) {
      update.$set.approvedAt = new Date();
    }

    // 如果更新审批状态为待审批，清除审批信息
    if (update.$set.approvalStatus === ApprovalStatus.PENDING) {
      update.$unset = update.$unset || {};
      update.$unset.approvedBy = 1;
      update.$unset.approvedAt = 1;
    }
  }

  next();
});

/**
 * 实例方法：转换为JSON格式
 * 用于API响应，隐藏敏感信息
 */
UserRoleSchema.methods.toJSON = function() {
  const obj = this.toObject();
  
  // 格式化日期
  if (obj.assignedAt) {
    obj.assignedAt = obj.assignedAt.toISOString();
  }
  if (obj.expiresAt) {
    obj.expiresAt = obj.expiresAt.toISOString();
  }
  if (obj.approvedAt) {
    obj.approvedAt = obj.approvedAt.toISOString();
  }
  if (obj.createdAt) {
    obj.createdAt = obj.createdAt.toISOString();
  }
  if (obj.updatedAt) {
    obj.updatedAt = obj.updatedAt.toISOString();
  }

  return obj;
};

/**
 * 实例方法：检查角色分配是否已过期
 */
UserRoleSchema.methods.isExpired = function(): boolean {
  if (!this.expiresAt) {
    return false;
  }
  return new Date() > this.expiresAt;
};

/**
 * 实例方法：检查角色分配是否需要审批
 */
UserRoleSchema.methods.needsApproval = function(): boolean {
  return this.approvalStatus === ApprovalStatus.PENDING;
};

/**
 * 实例方法：审批角色分配
 */
UserRoleSchema.methods.approve = async function(
  approvedBy: mongoose.Types.ObjectId, 
  approved: boolean
): Promise<IUserRoleDocument> {
  this.approvalStatus = approved ? ApprovalStatus.APPROVED : ApprovalStatus.REJECTED;
  this.approvedBy = approvedBy;
  this.approvedAt = new Date();
  
  return await this.save();
};

/**
 * 实例方法：检查是否可以被删除
 */
UserRoleSchema.methods.canBeDeleted = function(): boolean {
  // 已审批的角色分配可以被删除（撤销）
  // 待审批的角色分配可以被删除（取消）
  // 已拒绝的角色分配可以被删除（清理）
  return true;
};

/**
 * 静态方法：根据用户ID查找角色关联
 */
UserRoleSchema.statics.findByUserId = function(
  userId: mongoose.Types.ObjectId, 
  systemId?: mongoose.Types.ObjectId
): Promise<IUserRoleDocument[]> {
  const query: any = { userId, status: 1 };
  if (systemId) {
    query.systemId = systemId;
  }
  return this.find(query).populate('roleId').populate('systemId').sort({ assignedAt: -1 });
};

/**
 * 静态方法：根据角色ID查找用户关联
 */
UserRoleSchema.statics.findByRoleId = function(
  roleId: mongoose.Types.ObjectId, 
  systemId?: mongoose.Types.ObjectId
): Promise<IUserRoleDocument[]> {
  const query: any = { roleId, status: 1 };
  if (systemId) {
    query.systemId = systemId;
  }
  return this.find(query).populate('userId').populate('systemId').sort({ assignedAt: -1 });
};

/**
 * 静态方法：根据系统ID查找角色关联
 */
UserRoleSchema.statics.findBySystemId = function(
  systemId: mongoose.Types.ObjectId
): Promise<IUserRoleDocument[]> {
  return this.find({ systemId, status: 1 })
    .populate('userId')
    .populate('roleId')
    .sort({ assignedAt: -1 });
};

/**
 * 静态方法：检查用户是否拥有指定角色
 */
UserRoleSchema.statics.hasRole = async function(
  userId: mongoose.Types.ObjectId, 
  roleId: mongoose.Types.ObjectId, 
  systemId?: mongoose.Types.ObjectId
): Promise<boolean> {
  const query: any = {
    userId,
    roleId,
    status: 1,
    approvalStatus: ApprovalStatus.APPROVED
  };
  
  if (systemId) {
    query.systemId = systemId;
  }
  
  // 检查是否过期
  query.$or = [
    { expiresAt: { $exists: false } },
    { expiresAt: null },
    { expiresAt: { $gt: new Date() } }
  ];
  
  const userRole = await this.findOne(query);
  return !!userRole;
};

/**
 * 静态方法：批量分配角色
 */
UserRoleSchema.statics.batchAssign = async function(
  assignments: Partial<IUserRole>[]
): Promise<IUserRoleDocument[]> {
  const validAssignments = assignments.map(assignment => ({
    ...assignment,
    assignedAt: assignment.assignedAt || new Date(),
    approvalStatus: assignment.approvalStatus || ApprovalStatus.APPROVED,
    status: assignment.status !== undefined ? assignment.status : 1
  }));
  
  return await this.insertMany(validAssignments, { ordered: false });
};

/**
 * 静态方法：批量撤销角色
 */
UserRoleSchema.statics.batchRevoke = async function(
  userId: mongoose.Types.ObjectId, 
  roleIds: mongoose.Types.ObjectId[], 
  systemId?: mongoose.Types.ObjectId
): Promise<number> {
  const query: any = { userId, roleId: { $in: roleIds } };
  if (systemId) {
    query.systemId = systemId;
  }
  
  const result = await this.deleteMany(query);
  return result.deletedCount || 0;
};

/**
 * 静态方法：获取用户在指定系统的所有角色
 */
UserRoleSchema.statics.getUserRolesInSystem = function(
  userId: mongoose.Types.ObjectId, 
  systemId: mongoose.Types.ObjectId
): Promise<IUserRoleDocument[]> {
  return this.find({
    userId,
    systemId,
    status: 1,
    approvalStatus: ApprovalStatus.APPROVED,
    $or: [
      { expiresAt: { $exists: false } },
      { expiresAt: null },
      { expiresAt: { $gt: new Date() } }
    ]
  }).populate('roleId').sort({ assignedAt: -1 });
};

/**
 * 静态方法：获取待审批的角色分配
 */
UserRoleSchema.statics.getPendingApprovals = function(
  systemId?: mongoose.Types.ObjectId
): Promise<IUserRoleDocument[]> {
  const query: any = {
    status: 1,
    approvalStatus: ApprovalStatus.PENDING
  };
  
  if (systemId) {
    query.systemId = systemId;
  }
  
  return this.find(query)
    .populate('userId')
    .populate('roleId')
    .populate('assignedBy')
    .sort({ assignedAt: -1 });
};

/**
 * 静态方法：获取即将过期的角色分配
 */
UserRoleSchema.statics.getExpiringRoles = function(days: number): Promise<IUserRoleDocument[]> {
  const futureDate = new Date();
  futureDate.setDate(futureDate.getDate() + days);
  
  return this.find({
    status: 1,
    approvalStatus: ApprovalStatus.APPROVED,
    expiresAt: {
      $exists: true,
      $ne: null,
      $lte: futureDate,
      $gt: new Date()
    }
  })
  .populate('userId')
  .populate('roleId')
  .sort({ expiresAt: 1 });
};

/**
 * 静态方法：清理过期的角色分配
 */
UserRoleSchema.statics.cleanupExpiredRoles = async function(): Promise<number> {
  const result = await this.updateMany(
    {
      status: 1,
      expiresAt: {
        $exists: true,
        $ne: null,
        $lt: new Date()
      }
    },
    {
      $set: { status: 0 }
    }
  );
  
  return result.modifiedCount || 0;
};

// 导出模型
export const UserRole = mongoose.model<IUserRoleDocument, IUserRoleModel>('UserRole', UserRoleSchema);

export default UserRole;